کاوش الگوهای طراحی آفرینشی پایتون: Singleton, Factory, Abstract Factory, Builder و Prototype. پیادهسازیها، مزایا و کاربردهای دنیای واقعی آنها را بیاموزید.
الگوهای طراحی پایتون: مروری عمیق بر الگوهای آفرینشی
الگوهای طراحی راهحلهای قابل استفاده مجدد برای مشکلات رایج در طراحی نرمافزار هستند. آنها یک طرح کلی برای نحوه حل این مشکلات ارائه میدهند و باعث ارتقای قابلیت استفاده مجدد کد، قابلیت نگهداری و انعطافپذیری میشوند. الگوهای طراحی آفرینشی، به طور خاص، با مکانیسمهای ایجاد شیء سروکار دارند و سعی میکنند اشیاء را به روشی مناسب برای موقعیت ایجاد کنند. این مقاله یک بررسی جامع از الگوهای طراحی آفرینشی در پایتون، شامل توضیحات دقیق، نمونههای کد و کاربردهای عملی مرتبط با مخاطبان جهانی ارائه میدهد.
الگوهای طراحی آفرینشی چیستند؟
الگوهای طراحی آفرینشی فرآیند نمونهسازی را انتزاعی میکنند. آنها کد کلاینت را از کلاسهای خاصی که نمونهسازی میشوند جدا میکنند و امکان انعطافپذیری و کنترل بیشتر بر ایجاد شیء را فراهم میکنند. با استفاده از این الگوها، میتوانید اشیاء را بدون مشخص کردن کلاس دقیق شیء که ایجاد میشود، ایجاد کنید. این تفکیک نگرانیها، کد را قویتر و نگهداری آن را آسانتر میکند.
هدف اصلی الگوهای آفرینشی، انتزاعی کردن فرآیند نمونهسازی شیء است و پیچیدگیهای ایجاد شیء را از مشتری پنهان میکند. این به توسعهدهندگان اجازه میدهد تا بر منطق سطح بالای برنامههای خود تمرکز کنند، بدون اینکه در جزئیات ریز و درشت ایجاد شیء غرق شوند.
انواع الگوهای طراحی آفرینشی
ما الگوهای طراحی آفرینشی زیر را در این مقاله پوشش خواهیم داد:
- Singleton: اطمینان حاصل میکند که یک کلاس فقط یک نمونه دارد و یک نقطه دسترسی جهانی به آن فراهم میکند.
- Factory Method: یک رابط برای ایجاد یک شیء تعریف میکند، اما به زیر کلاسها اجازه میدهد تا تصمیم بگیرند که کدام کلاس را نمونهسازی کنند.
- Abstract Factory: یک رابط برای ایجاد خانوادهای از اشیاء مرتبط یا وابسته بدون مشخص کردن کلاسهای بتنی آنها فراهم میکند.
- Builder: ساخت یک شیء پیچیده را از نمایش آن جدا میکند و به فرآیند ساخت یکسان اجازه میدهد تا نمایشهای متفاوتی ایجاد کند.
- Prototype: نوع اشیایی را که باید با استفاده از یک نمونه اولیه ایجاد شوند، مشخص میکند و اشیاء جدید را با کپی کردن این نمونه اولیه ایجاد میکند.
1. الگوی Singleton
الگوی Singleton تضمین میکند که یک کلاس فقط یک نمونه دارد و یک نقطه دسترسی جهانی به آن ارائه میدهد. این الگو زمانی مفید است که دقیقاً به یک شیء برای هماهنگی اقدامات در سراسر سیستم نیاز باشد. اغلب برای مدیریت منابع، ثبت وقایع یا تنظیمات پیکربندی استفاده میشود.
پیادهسازی
در اینجا یک پیادهسازی پایتون از الگوی Singleton آمده است:
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return cls._instance
# Example usage
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # Output: True
توضیحات:
_instance: این متغیر کلاس، یک نمونه از کلاس را ذخیره میکند.__new__: این متد قبل از__init__، هنگام ایجاد یک شیء فراخوانی میشود. بررسی میکند که آیا یک نمونه از قبل وجود دارد یا خیر. اگر نه، یک نمونه جدید با استفاده ازsuper().__new__(cls)ایجاد میکند و آن را در_instanceذخیره میکند. اگر یک نمونه از قبل وجود داشته باشد، نمونه موجود را برمیگرداند.
موارد استفاده
- اتصال به پایگاه داده: اطمینان از باز بودن فقط یک اتصال به پایگاه داده در یک زمان.
- مدیر پیکربندی: ارائه یک نقطه دسترسی واحد به تنظیمات پیکربندی برنامه.
- Logger: ایجاد یک نمونه ورود به سیستم واحد برای مدیریت تمام عملیات ورود به سیستم در برنامه.
مثال
بیایید یک مثال ساده از یک مدیر پیکربندی که با استفاده از الگوی Singleton پیادهسازی شده است، در نظر بگیریم:
class ConfigurationManager(Singleton):
def __init__(self):
if not hasattr(self, 'config'): # Ensure __init__ is only called once
self.config = {}
def set_config(self, key, value):
self.config[key] = value
def get_config(self, key):
return self.config.get(key)
# Example usage
config_manager1 = ConfigurationManager()
config_manager1.set_config('database_url', 'localhost:5432')
config_manager2 = ConfigurationManager()
print(config_manager2.get_config('database_url')) # Output: localhost:5432
2. الگوی Factory Method
الگوی Factory Method یک رابط برای ایجاد یک شیء تعریف میکند، اما به زیر کلاسها اجازه میدهد تا تصمیم بگیرند که کدام کلاس را نمونهسازی کنند. Factory Method به یک کلاس اجازه میدهد تا نمونهسازی را به زیر کلاسها موکول کند. این الگو، اتصال سست را ترویج میدهد و به شما امکان میدهد انواع محصول جدید را بدون تغییر کد موجود اضافه کنید.
پیادهسازی
در اینجا یک پیادهسازی پایتون از الگوی Factory Method آمده است:
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
class AnimalFactory(ABC):
@abstractmethod
def create_animal(self):
pass
class DogFactory(AnimalFactory):
def create_animal(self):
return Dog()
class CatFactory(AnimalFactory):
def create_animal(self):
return Cat()
# Client code
def get_animal(factory: AnimalFactory):
animal = factory.create_animal()
return animal.speak()
dog_sound = get_animal(DogFactory())
cat_sound = get_animal(CatFactory())
print(f"Dog says: {dog_sound}") # Output: Dog says: Woof!
print(f"Cat says: {cat_sound}") # Output: Cat says: Meow!
توضیحات:
Animal: یک کلاس پایه انتزاعی که رابط را برای تمام انواع حیوانات تعریف میکند.DogوCat: کلاسهای بتنی که رابطAnimalرا پیادهسازی میکنند.AnimalFactory: یک کلاس پایه انتزاعی که رابط را برای ایجاد حیوانات تعریف میکند.DogFactoryوCatFactory: کلاسهای بتنی که رابطAnimalFactoryرا پیادهسازی میکنند، که مسئول ایجاد نمونههایDogوCatهستند.get_animal: یک تابع کلاینت که از factory برای ایجاد و استفاده از یک حیوان استفاده میکند.
موارد استفاده
- چارچوبهای UI: ایجاد عناصر UI مخصوص پلتفرم (مانند دکمهها، فیلدهای متنی) با استفاده از فکتوریهای مختلف برای سیستمعاملهای مختلف.
- توسعه بازی: ایجاد انواع مختلف شخصیتها یا اشیاء بازی بر اساس سطح بازی یا انتخاب کاربر.
- پردازش اسناد: ایجاد انواع مختلف اسناد (مانند PDF، Word، HTML) با استفاده از فکتوریهای مختلف بر اساس قالب خروجی مورد نظر.
مثال
سناریویی را در نظر بگیرید که میخواهید انواع مختلف روشهای پرداخت را بر اساس انتخاب کاربر ایجاد کنید. در اینجا نحوه پیادهسازی این کار با استفاده از الگوی Factory Method آمده است:
from abc import ABC, abstractmethod
class Payment(ABC):
@abstractmethod
def process_payment(self, amount):
pass
class CreditCardPayment(Payment):
def process_payment(self, amount):
return f"Processing credit card payment of ${amount}"
class PayPalPayment(Payment):
def process_payment(self, amount):
return f"Processing PayPal payment of ${amount}"
class PaymentFactory(ABC):
@abstractmethod
def create_payment_method(self):
pass
class CreditCardPaymentFactory(PaymentFactory):
def create_payment_method(self):
return CreditCardPayment()
class PayPalPaymentFactory(PaymentFactory):
def create_payment_method(self):
return PayPalPayment()
# Client code
def process_payment(factory: PaymentFactory, amount):
payment_method = factory.create_payment_method()
return payment_method.process_payment(amount)
credit_card_payment = process_payment(CreditCardPaymentFactory(), 100)
paypal_payment = process_payment(PayPalPaymentFactory(), 50)
print(credit_card_payment) # Output: Processing credit card payment of $100
print(paypal_payment) # Output: Processing PayPal payment of $50
3. الگوی Abstract Factory
الگوی Abstract Factory یک رابط برای ایجاد خانوادهای از اشیاء مرتبط یا وابسته بدون مشخص کردن کلاسهای بتنی آنها فراهم میکند. این به شما امکان میدهد اشیایی را ایجاد کنید که برای کار با یکدیگر طراحی شدهاند و از سازگاری و ثبات اطمینان حاصل کنید.
پیادهسازی
در اینجا یک پیادهسازی پایتون از الگوی Abstract Factory آمده است:
from abc import ABC, abstractmethod
class Button(ABC):
@abstractmethod
def paint(self):
pass
class Checkbox(ABC):
@abstractmethod
def paint(self):
pass
class GUIFactory(ABC):
@abstractmethod
def create_button(self):
pass
@abstractmethod
def create_checkbox(self):
pass
class WinFactory(GUIFactory):
def create_button(self):
return WinButton()
def create_checkbox(self):
return WinCheckbox()
class MacFactory(GUIFactory):
def create_button(self):
return MacButton()
def create_checkbox(self):
return MacCheckbox()
class WinButton(Button):
def paint(self):
return "Rendering a Windows button"
class MacButton(Button):
def paint(self):
return "Rendering a Mac button"
class WinCheckbox(Checkbox):
def paint(self):
return "Rendering a Windows checkbox"
class MacCheckbox(Checkbox):
def paint(self):
return "Rendering a Mac checkbox"
# Client code
def paint_ui(factory: GUIFactory):
button = factory.create_button()
checkbox = factory.create_checkbox()
return button.paint(), checkbox.paint()
win_button, win_checkbox = paint_ui(WinFactory())
mac_button, mac_checkbox = paint_ui(MacFactory())
print(win_button) # Output: Rendering a Windows button
print(win_checkbox) # Output: Rendering a Windows checkbox
print(mac_button) # Output: Rendering a Mac button
print(mac_checkbox) # Output: Rendering a Mac checkbox
توضیحات:
ButtonوCheckbox: کلاسهای پایه انتزاعی که رابطها را برای عناصر UI تعریف میکنند.WinButton،MacButton،WinCheckboxوMacCheckbox: کلاسهای بتنی که رابطهای عنصر UI را برای پلتفرمهای Windows و Mac پیادهسازی میکنند.GUIFactory: یک کلاس پایه انتزاعی که رابط را برای ایجاد خانوادههای عناصر UI تعریف میکند.WinFactoryوMacFactory: کلاسهای بتنی که رابطGUIFactoryرا پیادهسازی میکنند، که مسئول ایجاد عناصر UI برای پلتفرمهای Windows و Mac هستند.paint_ui: یک تابع کلاینت که از فکتوری برای ایجاد و ترسیم عناصر UI استفاده میکند.
موارد استفاده
- چارچوبهای UI: ایجاد عناصر UI که با ظاهر و حس یک سیستم عامل یا پلتفرم خاص سازگار هستند.
- توسعه بازی: ایجاد اشیاء بازی که با سبک یک سطح یا تم خاص بازی سازگار هستند.
- دسترسی به دادهها: ایجاد اشیاء دسترسی به دادهها که با یک پایگاه داده یا منبع داده خاص سازگار هستند.
مثال
سناریویی را در نظر بگیرید که میخواهید انواع مختلف مبلمان (مانند صندلی، میز) را با سبکهای مختلف (مانند مدرن، ویکتوریایی) ایجاد کنید. در اینجا نحوه پیادهسازی این کار با استفاده از الگوی Abstract Factory آمده است:
from abc import ABC, abstractmethod
class Chair(ABC):
@abstractmethod
def create(self):
pass
class Table(ABC):
@abstractmethod
def create(self):
pass
class FurnitureFactory(ABC):
@abstractmethod
def create_chair(self):
pass
@abstractmethod
def create_table(self):
pass
class ModernFurnitureFactory(FurnitureFactory):
def create_chair(self):
return ModernChair()
def create_table(self):
return ModernTable()
class VictorianFurnitureFactory(FurnitureFactory):
def create_chair(self):
return VictorianChair()
def create_table(self):
return VictorianTable()
class ModernChair(Chair):
def create(self):
return "Creating a modern chair"
class VictorianChair(Chair):
def create(self):
return "Creating a Victorian chair"
class ModernTable(Table):
def create(self):
return "Creating a modern table"
class VictorianTable(Table):
def create(self):
return "Creating a Victorian table"
# Client code
def create_furniture(factory: FurnitureFactory):
chair = factory.create_chair()
table = factory.create_table()
return chair.create(), table.create()
modern_chair, modern_table = create_furniture(ModernFurnitureFactory())
victorian_chair, victorian_table = create_furniture(VictorianFurnitureFactory())
print(modern_chair) # Output: Creating a modern chair
print(modern_table) # Output: Creating a modern table
print(victorian_chair) # Output: Creating a Victorian chair
print(victorian_table) # Output: Creating a Victorian table
4. الگوی Builder
الگوی Builder ساخت یک شیء پیچیده را از نمایش آن جدا میکند و به فرآیند ساخت یکسان اجازه میدهد تا نمایشهای متفاوتی ایجاد کند. این زمانی مفید است که شما نیاز به ایجاد اشیاء پیچیده با چندین مؤلفه اختیاری دارید و میخواهید از ایجاد تعداد زیادی سازنده یا پارامتر پیکربندی جلوگیری کنید.
پیادهسازی
در اینجا یک پیادهسازی پایتون از الگوی Builder آمده است:
class Pizza:
def __init__(self):
self.dough = None
self.sauce = None
self.topping = None
def __str__(self):
return f"Pizza with dough: {self.dough}, sauce: {self.sauce}, and topping: {self.topping}"
class PizzaBuilder:
def __init__(self):
self.pizza = Pizza()
def set_dough(self, dough):
self.pizza.dough = dough
return self
def set_sauce(self, sauce):
self.pizza.sauce = sauce
return self
def set_topping(self, topping):
self.pizza.topping = topping
return self
def build(self):
return self.pizza
# Client code
pizza_builder = PizzaBuilder()
pizza = pizza_builder.set_dough("Thin crust").set_sauce("Tomato").set_topping("Pepperoni").build()
print(pizza) # Output: Pizza with dough: Thin crust, sauce: Tomato, and topping: Pepperoni
توضیحات:
Pizza: یک کلاس که نشان دهنده شیء پیچیده است که باید ساخته شود.PizzaBuilder: یک کلاس builder که متدهایی را برای تنظیم اجزای مختلف شیءPizzaفراهم میکند.
موارد استفاده
- تولید اسناد: ایجاد اسناد پیچیده (مانند گزارشها، فاکتورها) با بخشها و گزینههای قالببندی مختلف.
- توسعه بازی: ایجاد اشیاء پیچیده بازی (مانند شخصیتها، سطوح) با ویژگیها و مؤلفههای مختلف.
- پردازش دادهها: ایجاد ساختارهای دادهای پیچیده (مانند گرافها، درختان) با گرهها و روابط مختلف.
مثال
سناریویی را در نظر بگیرید که میخواهید انواع مختلفی از رایانهها را با اجزای مختلف (مانند CPU، RAM، فضای ذخیرهسازی) بسازید. در اینجا نحوه پیادهسازی این کار با استفاده از الگوی Builder آمده است:
class Computer:
def __init__(self):
self.cpu = None
self.ram = None
self.storage = None
self.graphics_card = None
def __str__(self):
return f"Computer with CPU: {self.cpu}, RAM: {self.ram}, Storage: {self.storage}, Graphics Card: {self.graphics_card}"
class ComputerBuilder:
def __init__(self):
self.computer = Computer()
def set_cpu(self, cpu):
self.computer.cpu = cpu
return self
def set_ram(self, ram):
self.computer.ram = ram
return self
def set_storage(self, storage):
self.computer.storage = storage
return self
def set_graphics_card(self, graphics_card):
self.computer.graphics_card = graphics_card
return self
def build(self):
return self.computer
# Client code
computer_builder = ComputerBuilder()
computer = computer_builder.set_cpu("Intel i7").set_ram("16GB").set_storage("1TB SSD").set_graphics_card("Nvidia RTX 3080").build()
print(computer)
# Output: Computer with CPU: Intel i7, RAM: 16GB, Storage: 1TB SSD, Graphics Card: Nvidia RTX 3080
5. الگوی Prototype
الگوی Prototype نوع اشیایی را که باید با استفاده از یک نمونه اولیه ایجاد شوند، مشخص میکند و اشیاء جدید را با کپی کردن این نمونه اولیه ایجاد میکند. این به شما امکان میدهد اشیاء جدید را با کلون کردن یک شیء موجود ایجاد کنید و از نیاز به ایجاد اشیاء از ابتدا جلوگیری کنید. این میتواند هنگام ایجاد اشیاء گرانقیمت یا پیچیده مفید باشد.
پیادهسازی
در اینجا یک پیادهسازی پایتون از الگوی Prototype آمده است:
import copy
class Prototype:
def __init__(self):
self._objects = {}
def register_object(self, name, obj):
self._objects[name] = obj
def unregister_object(self, name):
del self._objects[name]
def clone(self, name, **attrs):
obj = copy.deepcopy(self._objects.get(name))
if attrs:
obj.__dict__.update(attrs)
return obj
class Car:
def __init__(self):
self.name = ""
self.color = ""
self.options = []
def __str__(self):
return f"Car: Name={self.name}, Color={self.color}, Options={self.options}"
# Client code
prototype = Prototype()
car = Car()
car.name = "Generic Car"
car.color = "White"
car.options = ["AC", "GPS"]
prototype.register_object("generic", car)
car1 = prototype.clone("generic", name="Sports Car", color="Red", options=["AC", "GPS", "Spoiler"])
car2 = prototype.clone("generic", name="Family Car", color="Blue", options=["AC", "GPS", "Sunroof"])
print(car1)
# Output: Car: Name=Sports Car, Color=Red, Options=['AC', 'GPS', 'Spoiler']
print(car2)
# Output: Car: Name=Family Car, Color=Blue, Options=['AC', 'GPS', 'Sunroof']
توضیحات:
Prototype: کلاسی که نمونههای اولیه را مدیریت میکند و یک متد برای کلون کردن آنها ارائه میدهد.Car: کلاسی که شیء را برای کلون شدن نشان میدهد.
موارد استفاده
- توسعه بازی: ایجاد اشیاء بازی که شبیه به یکدیگر هستند، مانند دشمنان یا قدرتافزاییها.
- پردازش اسناد: ایجاد اسنادی که بر اساس یک الگو هستند.
- مدیریت پیکربندی: ایجاد اشیاء پیکربندی که بر اساس یک پیکربندی پیشفرض هستند.
مثال
سناریویی را در نظر بگیرید که میخواهید انواع مختلفی از کارمندان را با ویژگیهای مختلف (مانند نام، نقش، دپارتمان) ایجاد کنید. در اینجا نحوه پیادهسازی این کار با استفاده از الگوی Prototype آمده است:
import copy
class Employee:
def __init__(self):
self.name = None
self.role = None
self.department = None
def __str__(self):
return f"Employee: Name={self.name}, Role={self.role}, Department={self.department}"
class Prototype:
def __init__(self):
self._objects = {}
def register_object(self, name, obj):
self._objects[name] = obj
def unregister_object(self, name):
del self._objects[name]
def clone(self, name, **attrs):
obj = copy.deepcopy(self._objects.get(name))
if attrs:
obj.__dict__.update(attrs)
return obj
# Client code
prototype = Prototype()
employee = Employee()
employee.name = "Generic Employee"
employee.role = "Developer"
employee.department = "IT"
prototype.register_object("generic", employee)
employee1 = prototype.clone("generic", name="John Doe", role="Senior Developer")
employee2 = prototype.clone("generic", name="Jane Smith", role="Project Manager", department="Management")
print(employee1)
# Output: Employee: Name=John Doe, Role=Senior Developer, Department=IT
print(employee2)
# Output: Employee: Name=Jane Smith, Role=Project Manager, Department=Management
نتیجهگیری
الگوهای طراحی آفرینشی ابزارهای قدرتمندی را برای مدیریت ایجاد شیء به روشی انعطافپذیر و قابل نگهداری ارائه میدهند. با درک و اعمال این الگوها، میتوانید کد تمیزتر و قویتری بنویسید که گسترش و انطباق آن با الزامات در حال تغییر آسانتر است. این مقاله به بررسی پنج الگوی آفرینشی کلیدی — Singleton، Factory Method، Abstract Factory، Builder و Prototype — با مثالهای عملی و موارد استفاده در دنیای واقعی پرداخته است. تسلط بر این الگوها یک گام اساسی برای تبدیل شدن به یک توسعهدهنده پایتون ماهر است.
به یاد داشته باشید که انتخاب الگوی مناسب به مشکل خاصی که قصد حل آن را دارید بستگی دارد. هنگام انتخاب یک الگوی آفرینشی برای پروژه خود، پیچیدگی ایجاد شیء، نیاز به انعطافپذیری و پتانسیل برای تغییرات آینده را در نظر بگیرید. با انجام این کار، میتوانید از قدرت الگوهای طراحی برای ایجاد راهحلهای ظریف و کارآمد برای چالشهای رایج طراحی نرمافزار استفاده کنید.